package com.smart.expand.proxy.jdk;

import com.smart.expand.proxy.BaseProxy;
import com.smart.expand.proxy.AnnotationProcessor;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

/**
 * @author jiangjun
 */
public class JDKDynamicProxy extends BaseProxy implements InvocationHandler {

    private static JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy();

    private JDKDynamicProxy() {
    }

    public static JDKDynamicProxy getInstance(){
        return jdkDynamicProxy;
    }

    public <T> T newProxyInstance(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = "";
        Map<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> map = new HashMap<>();
        for (AnnotationProcessor<? extends Annotation> annotationProcessor : annotationProcessors) {
            Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ((ParameterizedType) annotationProcessor.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
            Annotation annotation1 = AnnotationUtils.findAnnotation(method, annotationClass);
            if (annotation1 != null) {
                map.put(annotationClass, annotationProcessor);
            }
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                Annotation annotation2 = AnnotationUtils.findAnnotation(parameter, annotationClass);
                if (annotation2 != null) {
                    args[i] = annotationProcessor.onParameter((Class) annotationClass, proxy, method, parameter, args[i]);
                }
            }
        }
        for (Map.Entry<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> classAnnotationProcessorEntry : map.entrySet()) {
            ret = classAnnotationProcessorEntry.getValue().onMethod((Class) classAnnotationProcessorEntry.getKey(), proxy, method, args);
            if (ret != null) {
                return ret;
            }
        }
        for (Map.Entry<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> classAnnotationProcessorEntry : map.entrySet()) {
            args = classAnnotationProcessorEntry.getValue().methodBefore((Class) classAnnotationProcessorEntry.getKey(), proxy, method, args);
        }
        for (Map.Entry<Class<? extends Annotation>, AnnotationProcessor<? extends Annotation>> classAnnotationProcessorEntry : map.entrySet()) {
            ret = classAnnotationProcessorEntry.getValue().methodAfter((Class) classAnnotationProcessorEntry.getKey(), proxy, method, ret);
        }
        return ret;
    }

}
